/* LwIP SNTP example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_sntp.h"

#include "lcd.h"

/*******************************************************
 *                Macros
 *******************************************************/

/*******************************************************
 *                Constants
 *******************************************************/
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1
#define MAXIMUM_RETRY_WIFI (5)
#define MAXIMUM_RETRY_SNTP (60)
// SNTP间隔30分钟
#define SNTP_INTERVAL      (30*60*1000)

/*******************************************************
 *                Variable Definitions
 *******************************************************/
static const char *TAG = "SNTP";
static int s_retry_num = 0;
static EventGroupHandle_t s_wifi_event_group;

/*******************************************************
 *                Function Declarations
 *******************************************************/
static void initialize_wifi(void);
static void initialize_sntp(void);
static void sntp_task(void *pv);
static void time_sync_notification_cb(struct timeval *tv);
static void print_time(void);

static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);

/*******************************************************
 *                Function Definitions
 *******************************************************/
/**
 * @brief sntp获取到时间后的回调函数，暂时无用
 * @param tv sntp获取到的时间
 */
static void time_sync_notification_cb(struct timeval *tv)
{
    ESP_LOGI(TAG, "Notification of a time synchronization event");
}

/**
 * @brief 打印当前时间
 */
static void print_time(void)
{
    time_t now;
    struct tm ti;
    char buf[64];

    EventGroupHandle_t handle;

    static char last_minute = -1;

    // 获取当前时间
    time(&now);
    localtime_r(&now, &ti);
    // 分钟数改变，需要刷新时间
    if( (char)ti.tm_min != last_minute ) {
        last_minute = (char)ti.tm_min;
        // 格式化时间
        strftime(buf, sizeof(buf), "%c", &ti);
        ESP_LOGI(TAG, "The Current time is: %s",buf);
        // TODO：在显示屏上显示时间
        handle = get_disp_handle();
        xEventGroupSetBits(handle, DISP_UPDATE_TIME_BIT);

    }
}

/**
 * @brief 每500ms获取一次当前时间，根据需要刷新时间显示
 */
static void sntp_task(void *pv)
{
    char retry = 0;

    setenv("TZ", "CST-8", 1);
    tzset();

    // 等待sntp获取到时间
    while(sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
        if(retry < MAXIMUM_RETRY_SNTP) {
            retry++;
            ESP_LOGI(TAG, "Waiting for system time to be set...");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        else {
            // 超过60秒仍未连上，可以认为网络有问题，重启
            esp_restart();
        }
    }

    // 循环打印时间
    while (1) {
        print_time();

        vTaskDelay(500 / portTICK_PERIOD_MS); /* 延迟500ms */
    }

    vTaskDelete(NULL); 
}

/**
 * @brief ESP事件的回调函数
 */
static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < MAXIMUM_RETRY_WIFI) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

/**
 * @brief 初始化WiFi并联网，启动sntp任务
 */
static void initialize_wifi(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    // 设定wifi配置
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = "MERCURY_3AD5",
            .password = "qwertyui",
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	        .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    // 等待WiFi连接信息
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap");
        // 设定sntp
        initialize_sntp();
        // 创建任务
        xTaskCreate(sntp_task, "SNTP_TASK", 2048, NULL, 5, NULL);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect");
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    // 取消注册
    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
    vEventGroupDelete(s_wifi_event_group); 
}

/**
 * @brief 初始化sntp的设定
 */
static void initialize_sntp(void)
{
    ESP_LOGI(TAG, "Initializing SNTP");

    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, "pool.ntp.org");
    sntp_set_time_sync_notification_cb(time_sync_notification_cb);
    sntp_set_sync_interval(SNTP_INTERVAL);

    sntp_init();
}

void app_main(void)
{
    //Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    initialize_wifi();

    display_init();
}